#include <QApplication>
#include <QHeaderView>
#include <QFileInfo>
#include <QMenu>
#include <QClipboard>
#include <Resources.h>
#include "IssuesBrowser.h"

#define ISSUES_COLUMN_WIDTH 500
#define FILE_COLUMN_WIDTH   200
#define LINE_COLUMN_WIDTH   100

#define TEXT_COLUMN_WIDTH(w) \
    (w - ISSUES_COLUMN_WIDTH - FILE_COLUMN_WIDTH - LINE_COLUMN_WIDTH * 2)

namespace Domy {

/**
 * @brief The IssuesColumn enum
 * @note  列号
 */
enum IssuesColumn {
    Column_Issues = 0,
    Column_LineText,
    Column_FileName,
    Column_Line,
    Column_Column
};

IssuesBrowser::IssuesBrowser(QWidget *parent)
    : QTreeWidget(parent),
      m_warning_visible(true),
      m_error_visible(true)
{
    this->setHeaderLabels(QStringList() << tr("Issues") << tr("Error Text") << tr("FileName") << tr("Line") << tr("Column"));
    this->header()->setVisible(false);
    this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    this->header()->setSectionResizeMode(QHeaderView::Fixed);
    this->header()->setStretchLastSection(true);
    this->setContextMenuPolicy(Qt::CustomContextMenu);

    connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(slotOnCustomContextMenuRequest(const QPoint &)));
}

IssuesBrowser::~IssuesBrowser()
{

}

void IssuesBrowser::appendWarningIssues(const Issues &issues)
{
    m_warning_list.append(issues);

    if (!m_warning_visible) {
        return;
    }

    QTreeWidgetItem *item = createItem(IssuesType_Warning, issues);

    this->addTopLevelItem(item);
}

void IssuesBrowser::appendErrorIssues(const Issues &issues)
{
    m_error_list.append(issues);

    if (!m_error_visible) {
        return;
    }

    QTreeWidgetItem *item = createItem(IssuesType_Error, issues);
    item->setIcon(Column_Issues, GET_ICON("error"));

    if (m_warning_list.isEmpty()) {
        this->addTopLevelItem(item);
    }
    else {
        this->insertTopLevelItem(m_error_list.count(), item);
    }
}

void IssuesBrowser::setWarningIssuesList(const QList<Issues> &warning_list)
{
    m_warning_list = warning_list;

    updateIssues();
}

void IssuesBrowser::setErrorIssuesList(const QList<Issues> &error_list)
{
    m_error_list = error_list;

    updateIssues();
}

void IssuesBrowser::setWarningIssuesListVisible(bool visible)
{
    m_warning_visible = visible;

    updateIssues();
}

void IssuesBrowser::setErrorIssuesListVisible(bool visible)
{
    m_error_visible = visible;

    updateIssues();
}

void IssuesBrowser::clearIssues()
{
    m_warning_list.clear();
    m_error_list.clear();

    updateIssues();
}

QByteArray IssuesBrowser::toByteArray()
{
    QByteArray data;

    auto create_text = [&](const QString& type, const Issues &issues)->QString {
        QString text = QString("[%1][File: %2][Line: %3 Column: %4][Error Text: %5]: %6")
                .arg(type)
                .arg(issues.file_name)
                .arg(issues.line)
                .arg(issues.column)
                .arg(issues.line_text)
                .arg(issues.text);
        return text;
    };

    if (m_error_visible) {
        for (const Issues &issues : m_error_list) {
            data.append(create_text("Error", issues));
            data.append('\n');
        }
    }

    if (m_warning_visible) {
        for (const Issues &issues : m_warning_list) {
            data.append(create_text("Warning", issues));
            data.append('\n');
        }
    }

    return data;
}

void IssuesBrowser::setLineColumnVisible(bool visible)
{
    this->setColumnHidden(Column_Line, !visible);
    this->setColumnHidden(Column_Column, !visible);
}

void IssuesBrowser::showEvent(QShowEvent *event)
{
    QTreeWidget::showEvent(event);

    updateColumnSize();
}

void IssuesBrowser::resizeEvent(QResizeEvent *event)
{
    QTreeWidget::resizeEvent(event);

    updateColumnSize();
}

void IssuesBrowser::slotOnCustomContextMenuRequest(const QPoint &pos)
{
    QTreeWidgetItem *item = this->itemAt(pos);
    if (!item) {
        return;
    }

    QMenu menu;
    QAction *act_copy = menu.addAction(GET_ICON("copy"), tr("Copy"));
    QAction *act_remove = menu.addAction(GET_ICON("backspace"), tr("Remove"));

    QAction *act = menu.exec(QCursor::pos());
    if (act == act_copy) {
        // copy
        QString text;
        text.append(item->text(Column_Issues) + " ");
        text.append(item->text(Column_LineText) + " ");
        text.append(item->text(Column_FileName) + " ");
        text.append(item->text(Column_Line) + " ");
        text.append(item->text(Column_Column));

        qApp->clipboard()->setText(text);
    }
    else if (act == act_remove) {
        // remove
        int index = this->indexOfTopLevelItem(item);
        if (m_error_visible && index < m_error_list.count()) {
            m_error_list.removeAt(index);
        }
        else if (m_warning_visible && index >= m_error_list.count()) {
            m_warning_list.removeAt(index - m_error_list.count());
        }
        else {

        }

        delete item;
        item = Q_NULLPTR;
    }
    else {

    }
}

void IssuesBrowser::updateIssues()
{
    this->clear();

    if (m_error_visible) {
        for (const Issues &issues : m_error_list) {
            QTreeWidgetItem *item = createItem(IssuesType_Error, issues);
            this->addTopLevelItem(item);
        }
    }

    if (m_warning_visible) {
        for (const Issues &issues : m_warning_list) {
            QTreeWidgetItem *item = createItem(IssuesType_Warning, issues);
            this->addTopLevelItem(item);
        }
    }
}

void IssuesBrowser::updateColumnSize()
{
    this->setColumnWidth(Column_Issues, ISSUES_COLUMN_WIDTH);
    this->setColumnWidth(Column_LineText, TEXT_COLUMN_WIDTH(this->width()));
    this->setColumnWidth(Column_FileName, FILE_COLUMN_WIDTH);
    this->setColumnWidth(Column_Line, LINE_COLUMN_WIDTH);
    this->setColumnWidth(Column_Column, LINE_COLUMN_WIDTH);
}

QTreeWidgetItem *IssuesBrowser::createItem(IssuesType type, const Issues &issues)
{
    QTreeWidgetItem *item = new QTreeWidgetItem();
    item->setText(Column_Issues, QString("[%1] %2: %3")
                  .arg(issues.time)
                  .arg(type == IssuesType_Warning ? tr("Warning") : tr("Error"))
                  .arg(issues.text));
    item->setText(Column_LineText, tr("Text: %1").arg(issues.line_text));

    QString file_name;
    QFileInfo fi(issues.file_name);
    if (fi.exists()) {
        file_name = fi.fileName().isEmpty() ? "Null" : fi.fileName();
    }
    item->setText(Column_FileName, tr("FileName: %1").arg(file_name));
    item->setText(Column_Line, tr("Line: %1").arg(issues.line >= 0 ? QString::number(issues.line) : "Null"));
    item->setText(Column_Column, tr("Column: %1").arg(issues.column >= 0 ? QString::number(issues.column) : "Null"));

    item->setToolTip(Column_Issues, item->text(Column_Issues));
    item->setToolTip(Column_LineText, item->text(Column_LineText));
    item->setToolTip(Column_FileName, issues.file_name);
    item->setToolTip(Column_Line, item->text(Column_Line));
    item->setToolTip(Column_Column, item->text(Column_Column));

    QColor color = Qt::black;
    QIcon icon;
    switch (type) {
    case IssuesType_Warning:
        color = QColor(236, 188, 29);
        icon = GET_ICON("warning");
        break;
    case IssuesType_Error:
        color = Qt::red;
        icon = GET_ICON("error");
        break;
    default:
        break;
    }

    item->setIcon(Column_Issues, icon);

    item->setTextColor(Column_Issues, color);
    item->setTextColor(Column_LineText, color);
    item->setTextColor(Column_FileName, color);
    item->setTextColor(Column_Line, color);
    item->setTextColor(Column_Column, color);

    return item;
}

} // namespace Domy
